package com.leyou.search.service.impl;

import com.leyou.common.exception.LyException;
import com.leyou.item.client.ItemClient;
import com.leyou.search.dto.SearchParamDTO;
import com.leyou.search.entity.Goods;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.service.SearchService;
import com.leyou.starter.elastic.entity.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author ShenMin
 * @date 2021/7/21 17:14
 * @description
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    GoodsRepository goodsRepository;

    @Autowired
    ItemClient itemClient;

    @Override
    public Mono<List<String>> suggest(String key) {
        if (StringUtils.isBlank(key)) {
            throw new LyException(400, "请求参数不能为空！");
        }
        return goodsRepository.suggestBySingleField("suggestion", key);
        //return Mono.create(sink -> sink.success(Arrays.asList("华为", "华硕", "花王")));
    }

    @Override
    public Mono<PageInfo<Goods>> pageQuery(SearchParamDTO searchParamDTO) {
        QueryBuilder queryBuilder = getMatchQueryBuilder(searchParamDTO);

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        sourceBuilder.query(queryBuilder);

        sourceBuilder.from(searchParamDTO.getFrom());
        sourceBuilder.size(searchParamDTO.getSize());

        return goodsRepository.queryBySourceBuilderForPageHighlight(sourceBuilder);
    }

    @Override
    public Mono<Map<String, List<? extends Object>>> filterQuery(SearchParamDTO searchParamDTO) {

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        sourceBuilder.query(getMatchQueryBuilder(searchParamDTO));
        String brandAgg = "BRAND_AGG";
        String categoryAgg = "CATEGORY_AGG";
        String specsAgg = "SPECS_AGG";
        String specsNameAgg = "SPECSNAME_AGG";
        String specsValueAgg = "SPECSVALUE_AGG";
        sourceBuilder.aggregation(AggregationBuilders.terms(brandAgg).field("brandId").size(100));
        sourceBuilder.aggregation(AggregationBuilders.terms(categoryAgg).field("categoryId").size(100));
        sourceBuilder.aggregation(
                AggregationBuilders
                        .nested(specsAgg, "specs")
                        .subAggregation(
                                AggregationBuilders.terms(specsNameAgg).field("specs.name").size(100)
                                        .subAggregation(
                                                AggregationBuilders.terms(specsValueAgg)
                                                        .field("specs.value").size(100))));

        return this.goodsRepository.aggregationBySourceBuilder(sourceBuilder).map(
                aggregations -> {
                    Map<String, List<?>> result = new LinkedHashMap<>();
                    Terms brandIdAgg = aggregations.get(brandAgg);
                    Terms categoryIdAgg = aggregations.get(categoryAgg);
                    Nested specsNestedAgg = aggregations.get(specsAgg);

                    List<Long> brandsIds = brandIdAgg.getBuckets().stream().map(bucket -> {
                        long brandId = bucket.getKeyAsNumber().longValue();
                        return brandId;
                    }).collect(Collectors.toList());

                    List<Long> categoryIds = categoryIdAgg.getBuckets().stream().map(bucket -> {
                        long categoryId = bucket.getKeyAsNumber().longValue();
                        return categoryId;
                    }).collect(Collectors.toList());

                    if (!CollectionUtils.isEmpty(categoryIds)) {
                        result.put("分类", this.itemClient.listCategoryByIds(categoryIds));
                    }
                    if (!CollectionUtils.isEmpty(brandsIds)) {
                        result.put("品牌", this.itemClient.listBrandByIds(brandsIds));
                    }

                    Terms nameAgg = specsNestedAgg.getAggregations().get(specsNameAgg);
                    nameAgg.getBuckets().forEach(nameBucket -> {
                        String specName = nameBucket.getKeyAsString();
                        Terms valueAgg = nameBucket.getAggregations().get(specsValueAgg);
                        List<String> specValueList = valueAgg.getBuckets().stream().map(valueBucket -> {
                            String specValue = ((Terms.Bucket) valueBucket).getKeyAsString();
                            return specValue;
                        }).collect(Collectors.toList());
                        result.put(specName, specValueList);
                    });
                    return result;
                }
        );
    }


    private QueryBuilder getMatchQueryBuilder(SearchParamDTO searchParamDTO) {
        String key = searchParamDTO.getKey();
        if (StringUtils.isEmpty(key)) {
            throw new LyException(400, "必须输入查询条件");
        }

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.matchQuery("title", key).operator(Operator.AND));
        Map<String, String> filters = searchParamDTO.getFilters();
        if (!CollectionUtils.isEmpty(filters)) {
            filters.entrySet().forEach(entry -> {
                String specName = entry.getKey();
                String specValue = entry.getValue();
                if ("品牌".equals(specName)) {
                    specName = "brandId";
                    boolQueryBuilder.filter(QueryBuilders.termQuery(specName, specValue));
                } else if ("分类".equals(specName)) {
                    specName = "categoryId";
                    boolQueryBuilder.filter(QueryBuilders.termQuery(specName, specValue));
                } else {
                    BoolQueryBuilder filterBool = QueryBuilders.boolQuery();
                    filterBool.must(QueryBuilders.termQuery("specs.name", specName));
                    filterBool.must(QueryBuilders.termQuery("specs.value", specValue));
                    boolQueryBuilder.filter(
                            QueryBuilders.nestedQuery("specs", filterBool, ScoreMode.None));
                }
            });
        }
        return boolQueryBuilder;
    }
}
